package jmp.client.player;

import java.util.*;
import java.io.*;
import javax.sound.sampled.*;

import jmp.client.event.*;
import jmp.client.util.*;

import org.tritonus.share.sampled.file.*;

public class Musica {

    private File arquivo;
    private SourceDataLine sDLine;
    private AudioInputStream aIOStream;
    private int length;
    private boolean continua;
    private List<MusicListener> ouvintes;
    private Estado estadoMusica;

    public Musica(File arquivo) throws UnsupportedAudioFileException, IOException,
            LineUnavailableException {
        this.arquivo = arquivo;
        ouvintes = new ArrayList<MusicListener>();
        
        this.novaMusica();
    }

    private void novaMusica() throws IOException, UnsupportedAudioFileException, LineUnavailableException {
        AudioInputStream in = AudioSystem.getAudioInputStream(this.arquivo);
        AudioInputStream din = null;
        if (in != null) {
            AudioFormat baseFormat = in.getFormat();
            AudioFormat decodedFormat = new AudioFormat(
                    AudioFormat.Encoding.PCM_SIGNED,
                    baseFormat.getSampleRate(), 16, baseFormat.getChannels(),
                    baseFormat.getChannels() * 2, baseFormat.getSampleRate(),
                    false);
            din = AudioSystem.getAudioInputStream(decodedFormat, in);
            sDLine = getLine(decodedFormat);
            aIOStream = din;
            
            this.estadoMusica = Estado.STOP;
        }
    }

    private SourceDataLine getLine(AudioFormat audioFormat) throws LineUnavailableException {
        DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
        SourceDataLine res = (SourceDataLine) AudioSystem.getLine(info);
        res.open(audioFormat);

        return res;
    }

    public Object getInformacaoAudio(String informacao) throws UnsupportedAudioFileException,
            IOException {

        AudioFileFormat baseFileFormat = AudioSystem.getAudioFileFormat(this.arquivo);
        Map<String, String> propriedade = ((TAudioFileFormat) baseFileFormat).properties();

        return propriedade.get(informacao);
    }

    public String toString() {
        return arquivo.toString();
    }
    
    public Estado getEstadoMusica(){
        return this.estadoMusica;
    }

    public void close() throws IOException {
        sDLine.drain();
        sDLine.stop();
        sDLine.close();
        aIOStream.close();
    }

    public void play() throws IOException, LineUnavailableException {
        this.fireComecouMusicListener(new MusicEvent(this));
        this.estadoMusica = Estado.PLAY;
        
        sDLine.start();
        
        this.continua = true;

        final byte[] data = new byte[4096];

        new Thread(new Runnable() {

            public void run() {
                try {
                    while (continua) {
                        length = aIOStream.read(data, 0, data.length);
                        if (length > -1) {
                            sDLine.write(data, 0, length);
                        } else {
                            fireTerminouMusicListener(new MusicEvent(getMusica()));
                        }
                    }
                } catch (IOException ex) {
                    ex.printStackTrace();

                }
            }
        }).start();
    }

    private Musica getMusica() {
        return this;
    }
    
    public File getArquivo() {
        return this.arquivo;
    }

    public void pause() {
        this.continua = false;
        this.estadoMusica = Estado.PAUSE;
        
        sDLine.stop();
    }

    public void stop() throws IOException, UnsupportedAudioFileException, LineUnavailableException {
        this.length = 0;
        this.continua = false;

        sDLine.drain();
        sDLine.stop();
        sDLine.close();
        aIOStream.close();

        this.novaMusica();
    }

    public void addMusicListener(MusicListener ouvinte) {
        ouvintes.add(ouvinte);
    }

    public void removeMusicListener(MusicListener ouvinte) {
        ouvintes.remove(ouvinte);
    }

    private void fireComecouMusicListener(MusicEvent me) {
        for (MusicListener ml : this.ouvintes) {
            ml.comecou(me);
        }
    }

    private void fireTerminouMusicListener(MusicEvent me) {
        for (MusicListener ml : this.ouvintes) {
            ml.terminou(me);
        }
    }
}
